// Copyright (c) 2010 SuccessFactors, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
//     * Redistributions of source code must retain the above
//       copyright notice, this list of conditions and the following
//       disclaimer.
//
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials
//       provided with the distribution.
//
//     * Neither the name of the SuccessFactors, Inc. nor the names of
//       its contributors may be used to endorse or promote products
//       derived from this software without specific prior written
//       permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.

package org.owasp.jxt.tag.jsp;

import java.util.Arrays;
import org.owasp.jxt.Attribute;
import org.owasp.jxt.Encoder;
import org.owasp.jxt.ReflectNode;
import org.owasp.jxt.TranslateContext;
import org.owasp.jxt.ValidationContext;

/**
 * JspUseBeanNode
 *
 * @author Jeffrey Ichnowski
 * @version $Revision: 8 $
 */
public final class JspUseBeanNode extends ReflectNode {

    // TODO: move to JSP-compatibility package?

    private String _id;
    private String _scope;
    private String _className;
    private String _type;
    private String _beanName;

    JspUseBeanNode() {
        super("useBean");
    }

    @Attribute(required=true)
    public void setId(String id) {
        _id = id;
    }

    @Attribute
    public void setClass(String cls) {
        _className = cls;
    }

    @Attribute(required=true, pattern="page|request|session|application")
    public void setScope(String scope) {
        _scope = scope;
    }

    @Attribute
    public void setType(String type) {
        _type = type;
    }

    @Attribute
    public void setBeanName(String beanName) {
        _beanName = beanName;
    }

    @Override
    protected void validateContent(ValidationContext ctx) {
//         if (_scope == null) {
//             ctx.error(this, "validation.attribute-required", "scope");
//         } else if (scopeToConst(_scope) == null) {
//             ctx.error(this, "validation.attribute-invalid",
//                       Arrays.asList("page", "request", "session", "application"));
//         }

        if (!(_className != null && _type == null && _beanName == null) &&
            !(_className != null && _type != null && _beanName == null) &&
            !(_className == null && _type != null && _beanName == null) &&
            !(_className == null && _type != null && _beanName != null))
        {
            ctx.error(this, "validation.attribute-combination",
                      Arrays.asList("class", "type", "beanName"));
        }
    }

    private static String scopeToConst(String scope) {
        if ("page".equals(scope)) {
            return "PAGE_SCOPE";
        } else if ("request".equals(scope)) {
            return "REQUEST_SCOPE";
        } else if ("session".equals(scope)) {
            return "SESSION_SCOPE";
        } else if ("application".equals(scope)) {
            return "APPLICATION_SCOPE";
        } else {
            return null;
        }
    }

    private static String scopeToSyncVar(String scope) {
        if ("page".equals(scope)) {
            return "pageContext";
        } else if ("request".equals(scope) || "session".equals(scope) || "application".equals(scope)) {
            return "scope";
        } else {
            return null;
        }
    }

    @Override
    public void translate(TranslateContext ctx) {
        // TODO: full semantics!

        ctx.translateLocationComment(this);

        if (_className != null || _beanName != null) {
            ctx.codeln("synchronized ("+scopeToSyncVar(_scope)+") {");
            ctx.indent();
            translateBody(ctx);
            ctx.outdent();
            ctx.codeln("}");
        } else {
            translateBody(ctx);
        }

        translateChildren(ctx);
    }

    private void translateBody(TranslateContext ctx) {
        String varType = _type;

        if (varType == null) {
            varType = _className;
        }

        String scope = scopeToConst(_scope);

        ctx.codeln(varType+" "+_id+" = ("+varType+")pageContext.getAttribute(\""+
                   Encoder.JAVA_STRING.apply(_id)+"\", PageContext."+scope+");");
        ctx.codeln("if (null == "+_id+") {");
        ctx.indent();
        if (true) {
            if (_beanName != null) {
                ctx.codeln("try {");
                ctx.indent();
                if (true) {
                    ctx.codeln(_id+" = ("+varType+")java.beans.Beans.instantiate(this.getClass().getClassLoader(), \""+
                               Encoder.JAVA_STRING.apply(_beanName)+"\");");
                }
                ctx.outdent();
                ctx.codeln("} catch (ClassNotFoundException e) {");
                ctx.indent();
                if (true) {
                    ctx.codeln("throw (InstantiationException)"+
                               "new InstantiationException(e.getMessage()).initCause(e);");
                }
                ctx.outdent();
                ctx.codeln("} catch (Exception e) {");
                ctx.indent();
                if (true) {
                    ctx.codeln("throw new ServletException(\"Could not create "+
                               Encoder.JAVA_STRING.apply(_beanName)+".\", e);");
                }
                ctx.outdent();
                ctx.codeln("}");

            } else if (_className != null) {
                ctx.codeln(_id+" = new "+_className+"();");
                ctx.codeln("pageContext.setAttribute(\""+
                           Encoder.JAVA_STRING.apply(_id)+"\", "+_id+", PageContext."+
                           scope+");");
            } else {
                ctx.codeln("throw new java.lang.InstantiationException(\""+
                           Encoder.JAVA_STRING.apply("bean \""+_id+"\" not found in "+_scope+" scope")+
                           "\");");
            }

            // TODO: double check semantics of jsp:useBean.  It looks
            // like the call to translateChildren(ctx) might belong
            // here. (e.g. in the "if null" condition)
        }
        ctx.outdent();
        ctx.codeln("}");
    }

} // JspUseBeanNode
